package main

import (
	"fmt"
	"github.com/hyperledger/fabric-chaincode-go/shim"
	"github.com/hyperledger/fabric-protos-go/peer"
	"strconv"
)

type SimpleChaincode struct {
}

// 初始化数据状态，实例化/升级链码时被自动调用
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) peer.Response {
	// println 函数的输出信息会出现在链码容器的日志中
	fmt.Println("Init...")

	// 获取用户传递给调用链码的所需参数
	//_, args := stub.GetFunctionAndParameters()

	var A, B string    // 两个账户
	var Aval, Bval int // 两个账户的余额
	var err error

	A = "A"                         // 账户 A 用户名
	Aval, err = strconv.Atoi("100") // 账户 A 余额
	if err != nil {
		return shim.Error("期望的资产为整数")
	}

	B = "B"                         // 账户 B 用户名
	Bval, err = strconv.Atoi("200") // 账户 B 余额
	if err != nil {
		return shim.Error("期望的资产为整数")
	}

	fmt.Printf("A 资产：%d, B 资产为：%d\n", Aval, Bval)

	// 将账户 A 的状态写入账本中
	err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
	if err != nil {
		return shim.Error(err.Error())
	}

	// 将账户 B 的状态写入账本中
	err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
	if err != nil {
		return shim.Error(err.Error())
	}

	// 一切成功，返回 nil（shim.Success）
	return shim.Success(nil)
}

// 对账本数据进行操作时(query, invoke)被自动调用
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
	fmt.Println("Invoke...")
	// 获取用户传递给调用链码的函数名称及参数
	function, args := stub.GetFunctionAndParameters()

	// 对获取到的函数名称进行判断
	if function == "transfer" {
		// 调用 invoke 函数实现转账操作
		return t.transfer(stub, args)
	} else if function == "delete" {
		// 调用 delete 函数实现账户注销
		return t.delete(stub, args)
	} else if function == "query" {
		// 调用 query 实现账户查询操作
		return t.query(stub, args)
	} else if function == "register" {
		// 调用 register 实现添加账户
		return t.register(stub, args)
	}
	// 传递的函数名出错，返回 shim.Error()
	return shim.Error("无效的调用函数名称。期望为 \"invoke\" \"delete\" \"query\"\"register\"")
}

func (t *SimpleChaincode) register(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args) != 2 {
		return shim.Error("参数错误，期望获得2个参数")
	}
	err := stub.PutState(args[0], []byte(args[1]))
	if err != nil {
		return shim.Error("注册失败")
	}
	return shim.Success(nil)
}

// 账户间转钱
func (t *SimpleChaincode) transfer(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	var A, B string    // 账户 A 和 B
	var Aval, Bval int // 账户余额
	var X int          // 转账金额
	var err error

	if len(args) != 3 {
		return shim.Error("参数错误，期望获得3个参数")
	}

	A = args[0] // 账户 A 用户名
	B = args[1] // 账户 B 用户名

	// 从账本中获取 A 的余额
	Avalbytes, err := stub.GetState(A)
	if err != nil {
		return shim.Error("获取状态失败")
	}
	if Avalbytes == nil {
		return shim.Error("用户不存在")
	}
	Aval, _ = strconv.Atoi(string(Avalbytes))

	// 从账本中获取 B 的余额
	Bvalbytes, err := stub.GetState(B)
	if err != nil {
		return shim.Error("获取状态失败")
	}
	if Bvalbytes == nil {
		return shim.Error("用户不存在")
	}
	Bval, _ = strconv.Atoi(string(Bvalbytes))

	// X 为 转账金额
	X, err = strconv.Atoi(args[2])
	if err != nil {
		return shim.Error("无效的交易金额，期望为一个整数值")
	}
	// 转账
	Aval = Aval - X
	Bval = Bval + X
	fmt.Printf("A 资产：%d, B 资产为：%d\n", Aval, Bval)

	// 更新转账后账本中 A 余额
	err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
	if err != nil {
		return shim.Error(err.Error())
	}

	// 更新转账后账本中 B 余额
	err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
	if err != nil {
		return shim.Error(err.Error())
	}

	return shim.Success(nil)
}

// 账户注销
func (t *SimpleChaincode) delete(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args) != 1 {
		return shim.Error("参数错误，期望获得1个参数")
	}

	A := args[0] // 账户用户名

	// 从账本中删除该账户状态
	err := stub.DelState(A)
	if err != nil {
		return shim.Error("获取状态失败")
	}

	return shim.Success(nil)
}

// 账户查询
func (t *SimpleChaincode) query(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	var A string
	var err error

	if len(args) != 1 {
		return shim.Error("参数错误，期望获得1个参数")
	}

	A = args[0] // 账户用户名

	// 从账本中获取该账户余额
	Avalbytes, err := stub.GetState(A)
	if err != nil {
		jsonResp := "{\"Error\":\"Failed to get state for " + A + "\"}"
		return shim.Error(jsonResp)
	}

	if Avalbytes == nil {
		jsonResp := "{\"Error\":\"Nil amount for " + A + "\"}"
		return shim.Error(jsonResp)
	}

	jsonResp := "{\"Name\":\"" + A + "\",\"Amount\":\"" + string(Avalbytes) + "\"}"
	fmt.Printf("Query Response:%s\n", jsonResp)
	// 返回转账金额
	return shim.Success(Avalbytes)
}

func main() {
	err := shim.Start(new(SimpleChaincode))
	if err != nil {
		fmt.Printf("启动链玛失败: %s", err)
	}
}
